import hashlib
from datetime import datetime
from flask_sqlalchemy import BaseQuery
from flask import abort, current_app
from flask_login import UserMixin, AnonymousUserMixin
from . import db, lm


class Permission:
    FOLLOW = 0x01
    COMMENT = 0x02
    WRITE_ARTICLES = 0x04
    MODERATE_COMMENTS = 0x08
    ADMINISTER = 0x80


class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    default = db.Column(db.Boolean, default=False, index=True)
    permissions = db.Column(db.Integer)
    users = db.relationship('User', backref='role', lazy='dynamic')

    @staticmethod
    def insert_roles():
        roles = {
            'User': (Permission.FOLLOW | Permission.COMMENT | Permission.WRITE_ARTICLES, True),
            'Moderator': (Permission.FOLLOW | Permission.COMMENT | Permission.WRITE_ARTICLES |
                          Permission.MODERATE_COMMENTS, False),
            'Administrator': (0xff, False)
        }
        for r in roles:
            role = Role.query.filter_by(name=r).first()
            if role is None:
                role = Role(name=r)
            role.permissions = roles[r][0]
            role.default = roles[r][1]
            db.session.add(role)
        db.session.commit()

    def __repr__(self):
        return '<Role %r>' % self.name


class UserQuery(BaseQuery):
    def authenticate(self, username, password):

        user = self.filter(db.or_(User.username == username,
                                  User.email == username)).first()

        if user:
            authenticated = user.check_password(password)
        else:
            authenticated = False

        return user, authenticated


    def get_by_username(self, username):
        user = self.filter(User.username==username).first()
        if user is None:
            abort(404)
        return user


class User(UserMixin, db.Model):
    __tablename__ = 'users'

    query_class = UserQuery
    PER_PAGE = 50

    MEMBER = 100

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), unique=True)
    nickname = db.Column(db.String(20))
    email = db.Column(db.String(100), unique=True, nullable=False)
    _password = db.Column("password", db.String(80), nullable=False)
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    activation_key = db.Column(db.String(40))
    date_joined = db.Column(db.DateTime, default=datetime.now)
    last_login = db.Column(db.DateTime, default=datetime.now)
    last_request = db.Column(db.DateTime, default=datetime.now)
    location = db.Column(db.String(64))
    about_me = db.Column(db.Text())
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    comments = db.relationship('Comment', backref='author', lazy='dynamic')

    def set_role(self):
        if self.role is None:
            if self.email == current_app.config['BLOG_ADMIN']:
                self.role = Role.query.filter_by(permissions=0xff).first()
            if self.role is None:
                self.role = Role.query.filter_by(default=True).first()

    def _get_password(self):
        return self._password

    def _set_password(self, password):
        self._password = hashlib.md5(password).hexdigest()

    password = db.synonym("_password",
                          descriptor=property(_get_password,
                                              _set_password))

    def check_password(self,password):
        if self.password is None:
            return False
        return self.password == hashlib.md5(password).hexdigest()

    def can(self, permissions):
        return self.role is not None and \
            (self.role.permissions & permissions) == permissions

    def is_administrator(self):
        return self.can(Permission.ADMINISTER)

    @staticmethod
    def check_username(username):
        if User.query.filter_by(username=username).first() == None:
            return True
        return False

    @staticmethod
    def check_email(email):
        if User.query.filter_by(email=email).first() == None:
            return True
        return False

    def __repr__(self):
        return '<User %r>' % (self.username)


class AnonymousUser(AnonymousUserMixin):
    def can(self, permissions):
        return False

    def is_administrator(self):
        return False

lm.anonymous_user = AnonymousUser

artile_tag = db.Table('artile_tag',
    db.Column('post_id', db.Integer, db.ForeignKey('posts.id')),
    db.Column('tag_id', db.Integer, db.ForeignKey('tags.id'))
)


class Post(db.Model):
    __tablename__ = 'posts'

    PER_PAGE = 40

    id = db.Column(db.Integer, primary_key=True)

    title = db.Column("title", db.Unicode(100), index=True)
    main_content = db.Column(db.UnicodeText)
    content = db.Column(db.UnicodeText)
    num_comments = db.Column(db.Integer, default=0)
    created_date = db.Column(db.DateTime, default=datetime.now)
    update_time = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
    comments = db.relationship('Comment', backref='post', lazy='dynamic')
    tags = db.relationship('Tag', secondary=artile_tag,
        backref=db.backref('post', lazy='dynamic'))

    author_id = db.Column(db.Integer, db.ForeignKey('users.id'))

    __mapper_args__ = {'order_by': id.desc()}


class Tag(db.Model):
    __tablename__ = 'tags'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), unique=True)


class Comment(db.Model):
    __tablename__ = "comments"

    id = db.Column(db.Integer, primary_key=True)

    disabled = db.Column(db.Boolean)
    author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    post_id = db.Column(db.Integer, db.ForeignKey('posts.id'))

    comment = db.Column(db.UnicodeText)
    created_date = db.Column(db.DateTime, index=True, default=datetime.now)

    ip = db.Column(db.Integer)
    parent_id = db.Column(db.Integer,
                          db.ForeignKey("comments.id"))

    parent = db.relationship('Comment', remote_side=[id])

    __mapper_args__ = {'order_by' : id.asc()}
